#include "sortfrm.h"
#include "core/generate.h"
#include "core/sort.h"
#include <cstring>
#include <ctime>

// 关联事件
wxBEGIN_EVENT_TABLE(SortFrame, wxFrame)
    EVT_MENU(idMenuAbout, SortFrame::OnAbout)
    EVT_MENU(idMenuQuit, SortFrame::OnQuit)
    EVT_BUTTON(idBtnGenerate,SortFrame::OnBtnGenerateClicked)
    EVT_BUTTON(idBtnSort,SortFrame::OnBtnSortClicked)
    EVT_BUTTON(idBtnSave,SortFrame::OnBtnSaveClicked)
    EVT_BUTTON(idBtnRandomPath, SortFrame::OnBtnRandomPathClicked)
    EVT_BUTTON(idBtnOrderedPath, SortFrame::OnBtnOrderPathClicked)
wxEND_EVENT_TABLE()

SortFrame::SortFrame(const wxString & title)
    : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(650, 500))
{
    CreateAndLayoutCtrl(); // 布置在界面上布置控件，进行布局
    SetInitParam();        // 设定一些需要初始化的参数
}

void SortFrame::CreateAndLayoutCtrl()
{
    wxMenuBar *menubar = new wxMenuBar;
    wxMenu *about = new wxMenu;
    about->Append(idMenuAbout, wxT("关于软件\tF1"));
    about->Append(idMenuQuit, wxT("退出\tAlt-F4"));
    menubar->Append(about, wxT("关于"));
    SetMenuBar(menubar);

    wxBoxSizer* mainbox = new wxBoxSizer(wxVERTICAL);
    wxPanel* panel = new wxPanel(this);
    wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer *hbox1 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer *hbox3 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer *hbox4 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer *hbox5 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer *hbox6 = new wxBoxSizer(wxHORIZONTAL);
    wxStaticBoxSizer* sbox1 = new wxStaticBoxSizer(new wxStaticBox(panel, wxID_ANY, wxT("生成随机数")), wxVERTICAL);
    wxStaticBoxSizer* sbox2 = new wxStaticBoxSizer(new wxStaticBox(panel, wxID_ANY, wxT("排序选项")), wxVERTICAL);
    wxStaticBoxSizer* sbox3 = new wxStaticBoxSizer(new wxStaticBox(panel, wxID_ANY, wxT("保存选项")), wxVERTICAL);
    wxStaticText* st1 = new wxStaticText(panel, wxID_ANY, wxT("欢迎使用排序算法演示系统！"));
    wxStaticText* st2 = new wxStaticText(sbox1->GetStaticBox(), wxID_ANY, wxT("随机数个数："));
    wxStaticText* st3 = new wxStaticText(sbox1->GetStaticBox(), wxID_ANY, wxT("范围："));
    wxStaticText* st4 = new wxStaticText(sbox1->GetStaticBox(), wxID_ANY, wxT("~"));
    wxStaticText* st5 = new wxStaticText(sbox2->GetStaticBox(), wxID_ANY, wxT("使用的排序算法"));
    wxStaticText* st6 = new wxStaticText(sbox3->GetStaticBox(), wxID_ANY, wxT("随机数据保存路径"));
    wxStaticText* st7 = new wxStaticText(sbox3->GetStaticBox(), wxID_ANY, wxT("排序数据保存路径"));

    spCount = new wxSpinCtrl(sbox1->GetStaticBox(), wxID_ANY, "1000", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 10000000);
    spMin = new wxSpinCtrl(sbox1->GetStaticBox(), wxID_ANY, "0", wxDefaultPosition, wxSize(70, -1), wxSP_ARROW_KEYS, 0, 1000000000);
    spMax = new wxSpinCtrl(sbox1->GetStaticBox(), wxID_ANY, "1100", wxDefaultPosition, wxSize(70, -1), wxSP_ARROW_KEYS, 0, 1000000000);
    ch = new wxChoice(sbox2->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, chChoices, 0);
    tcRandomPath = new wxTextCtrl(sbox3->GetStaticBox(), idTcRandomPath, wxEmptyString, wxDefaultPosition, wxSize(350, -1));
    tcOrderedPath = new wxTextCtrl(sbox3->GetStaticBox(), idTcOrderedPath, wxEmptyString, wxDefaultPosition, wxSize(350, -1));
    btnRandomPath = new wxButton(sbox3->GetStaticBox(), idBtnRandomPath, wxT("选择文件"));
    btnOrderedPath = new wxButton(sbox3->GetStaticBox(), idBtnOrderedPath, wxT("选择文件"));
    btnGenerate = new wxButton(panel, idBtnGenerate, wxT("生成随机数"));
    btnSort = new wxButton(panel, idBtnSort, wxT("运行排序算法"));
    btnSave = new wxButton(panel, idBtnSave, wxT("保存数据"));

    tcRandomPath->SetEditable(false); // 设置文本框不可编辑
    tcOrderedPath->SetEditable(false);

    // 设置部分控件的最小值
    hbox1->SetMinSize(wxSize(600, 150));
    sbox1->SetMinSize(wxSize(-1, 130));
    hbox3->SetMinSize(wxSize(-1, 30));
    sbox2->SetMinSize(wxSize(-1, 130));
    sbox3->SetMinSize(wxSize(600, 100));

    btnGenerate->SetMinSize(wxSize(120, 40));
    btnSort->SetMinSize(wxSize(140, 40));
    btnSave->SetMinSize(wxSize(120, 40));

    wxFont btnFont = wxFont(14, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
    btnGenerate->SetFont(btnFont);
    btnSort->SetFont(btnFont);
    btnSave->SetFont(btnFont);
    btnSave->Enable(false);
    btnSort->Enable(false);

    // 将控件添加到容器中
    st1->SetFont(wxFont(18, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD )); // 设置字体大小
    st1->SetForegroundColour(*wxRED);   // 设置文本颜色
    vbox->Add(st1, 0, wxALIGN_CENTER | wxALL, 15);

    hbox2->Add(st2, 0, wxALL, 5);
    hbox2->Add(spCount, 0, wxALL, 5);
    sbox1->Add(hbox2, 1, wxALIGN_CENTER, 5);

    hbox3->Add(st3, 0, wxALL, 5);
    hbox3->Add(spMin, 0, wxALL, 5);
    hbox3->Add(st4, 0, wxALL, 5);
    hbox3->Add(spMax, 0, wxALL, 5);
    sbox1->Add(hbox3, 1, wxALIGN_CENTER, 5);

    hbox1->Add(sbox1, 1, wxALIGN_CENTER | wxRIGHT, 5);

    sbox2->Add(st5, 0, wxALIGN_CENTER | wxALL, 10);
    sbox2->Add(ch, 0, wxALIGN_CENTER | wxALL, 10);
    hbox1->Add(sbox2, 1, wxALIGN_CENTER | wxLEFT, 5);

    vbox->Add(hbox1, 0, wxALIGN_CENTER | wxALL, 10);

    hbox4->Add(st6, 0, wxALIGN_CENTER | wxLEFT, 10);
    hbox4->Add(tcRandomPath, 0, wxALIGN_CENTER | wxLEFT, 10);
    hbox4->Add(btnRandomPath, 0, wxALIGN_CENTER | wxLEFT, 15);
    sbox3->Add(hbox4, 1, wxALIGN_CENTER | wxEXPAND, 5);

    hbox5->Add(st7, 0, wxALIGN_CENTER | wxLEFT, 10);
    hbox5->Add(tcOrderedPath, 0, wxALIGN_CENTER | wxLEFT, 10);
    hbox5->Add(btnOrderedPath, 0, wxALIGN_CENTER | wxLEFT, 15);
    sbox3->Add(hbox5, 1, wxALIGN_CENTER | wxEXPAND, 5);

    vbox->Add(sbox3, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 10);

    hbox6->Add(btnGenerate, 0, wxALIGN_CENTER, 15);
    hbox6->Add(btnSort, 0, wxALIGN_CENTER | wxLEFT, 50);
    hbox6->Add(btnSave, 0, wxALIGN_CENTER | wxLEFT, 50);
    vbox->Add(hbox6, 0, wxALIGN_CENTER | wxALL, 15);

    panel->SetSizerAndFit(vbox);
    mainbox->Add(panel, 1, wxEXPAND);
    SetSizerAndFit(mainbox);
    SetWindowStyle(wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX)); //设置窗口不能改变大小
    Centre();
}

void SortFrame::SetInitParam()
{
    wxString workpath = ::wxGetCwd();                       // 获取文件运行目录
    tcRandomPath->SetValue(workpath + "/random_data.txt");  // 设置默认随机数据保存路径
    tcOrderedPath->SetValue(workpath + "/sorted_data.txt"); // 设置默认已排序数据保存路径
    raw = nullptr;
    sorted = nullptr;

    wxString arr[] = { wxT("快速排序"),  wxT("插入排序"), wxT("归并排序"),
                wxT("选择排序"), wxT("堆排序"), wxT("希尔排序"), wxT("计数排序"),
                wxT("冒泡排序"), wxT("折半插入排序")};
    chChoices = wxArrayString(9, arr);
    ch->Set(chChoices);   // 设置下拉选项
    ch->SetSelection(0);  // 设置默认选项
}

void SortFrame::SaveData(wxString filename, int a[], int len)
{
    wxFile *file = new wxFile(filename, wxFile::write);
    file->Write(wxString::Format("%d\n", len)); // 将数据的个数写入文件
    for(int i = 0; i < len; i++)                // 循环将数据添加到文件中
    {
        file->Write(wxString::Format("%d ", a[i]));
        if(i % 20 == 19) file->Write("\n");
    }
    file->Flush();
    file->Close();
}

void SortFrame::SaveRandomData()
{
    if(raw != nullptr)
        SaveData(tcRandomPath->GetValue(), raw, data_len);
}

void SortFrame::SaveSortedData()
{
    if(sorted != nullptr)
        SaveData(tcOrderedPath->GetValue(), sorted, data_len);
}

void SortFrame::OnBtnGenerateClicked(wxCommandEvent& event)
{
    btnGenerate->Enable(false); // 设置按钮不可用

    // 从控件中获取要生成的随机数数量和范围
    int vcnt = spCount->GetValue();
    int vmin = spMin->GetValue();
    int vmax = spMax->GetValue();

    if(vmax < vmin)
    {
        btnGenerate->Enable(true); // 设置按钮可用
        wxMessageBox(wxT("选择的随机数范围错误！\n请重新更改随机数范围。"),
                     wxT("生成随机数错误"), wxOK | wxICON_ERROR | wxCENTER, this);
        return;
    }

    if(vcnt >= vmax - vmin + 1)
    {
        btnGenerate->Enable(true); // 设置按钮可用
        wxMessageBox(wxT("选择的随机数范围过小！\n请重新更改随机数数量和范围。"),
                     wxT("生成随机数错误"), wxOK | wxICON_ERROR | wxCENTER, this);
        return;
    }

    raw = new int[vcnt];        // 申请内存
    data_len = vcnt;            // 将数组长度保存

    int ret = generate_random_num(vcnt, vmin, vmax, raw, vcnt); // 生成随机数

    // 设置按钮可用
    btnGenerate->Enable(true);
    if(!ret) btnSort->Enable(true);
    if(!ret) btnSave->Enable(true);

    wxString str = wxT("随机数据生成");
    str.Append(!ret ? wxT("成功") : wxT("失败"));
    str.Append(wxT("！"));
    wxMessageBox(str, wxT("提示"), wxOK, this);
}

void SortFrame::OnBtnSortClicked(wxCommandEvent& event)
{
    btnSort->Enable(false);
    btnSort->SetLabel(wxT("正在排序..."));
    void (*func)(int[], int, int) = nullptr; // 函数指针，用于指向排序函数
    int index = ch->GetSelection();
    switch(index) // 根据选项选择相应的排序算法
    {
    case 0:
        func = quick_sort;
        break;
    case 1:
        func = insertion_sort;
        break;
    case 2:
        func = merge_sort;
        break;
    case 3:
        func = selection_sort;
        break;
    case 4:
        func = heap_sort;
        break;
    case 5:
        func = shell_sort;
        break;
    case 6:
        func = counting_sort;
        break;
    case 7:
        func = bubble_sort;
        break;
    case 8:
        func = b_insert_sort;
        break;
    default:
        break;
    }

    clock_t start, finish;
    double duration = 0.0;
    if(func != nullptr)
    {
        if(sorted != nullptr) { delete sorted; sorted = nullptr; } // 删除旧数据
        sorted = new int[data_len];                     // 申请内存用于存放已排序数据
        memcpy(sorted, raw, sizeof(int) * data_len);   // 将随机数据复制到数组
        start = clock();
        func(sorted, 0, data_len - 1);                  // 对数据进行排序
        finish = clock();
        duration = (double)(finish - start) / CLOCKS_PER_SEC; // 计算时间
    }
    btnSort->SetLabel(wxT("运行排序算法"));
    btnSort->Enable(true);
    wxString info;
    if(func != nullptr)
        info = wxT("对数据排序成功！\n使用算法：") + ch->GetString(index) + wxString::Format(wxT("\n排序用时：%.3f秒"), duration);
    else
        info = wxT("排序算法选择错误！");
    wxMessageBox(info, wxT("提示"), wxOK | wxICON_INFORMATION, this);
}

void SortFrame::OnBtnSaveClicked(wxCommandEvent& event)
{
    btnSave->Enable(false);
    btnSave->SetLabel(wxT("正在保存..."));
    SaveRandomData();       // 调用保存随机数据
    SaveSortedData();       // 调用保存已排序数据
    btnSave->SetLabel(wxT("保存数据"));
    btnSave->Enable(true);
    wxMessageBox(wxT("数据保存成功！"), wxT("提示"), wxOK, this);
}

// 1是随机数据文件位置，2是排序数据文件位置
void SortFrame::SaveFileDialog(int type=1)
{
    wxFileName filename;
    if(type == 1)
        filename = tcRandomPath->GetValue();
    else
        filename = tcOrderedPath->GetValue();

    wxFileDialog *saveFileDialog = new wxFileDialog(this, wxT("选择文件"), filename.GetPath(), filename.GetFullName(),
                wxT("txt files (*.txt)|*.txt"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
    if(saveFileDialog->ShowModal() == wxID_CANCEL) // 用户取消，没有选择文件的情况
    {
        return ;
    }
    if(type == 1)
        tcRandomPath->SetValue(saveFileDialog->GetPath());
    else
        tcOrderedPath->SetValue(saveFileDialog->GetPath());
    delete saveFileDialog;
}

void SortFrame::OnBtnRandomPathClicked(wxCommandEvent& event)
{
    SaveFileDialog(1); // 保存数据，1是保存随机数据
}

void SortFrame::OnBtnOrderPathClicked(wxCommandEvent& event)
{
    SaveFileDialog(2); // 保存数据，2表示保存已排序数据
}

void SortFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
    wxString str = wxT("2017数据结构作业\n作者：\n李锡泽( 201535020421 )\n高  正( 201535020406 )");
    wxMessageBox(str, "关于软件", wxOK, this);
}

void SortFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
    Close(); // 关闭窗口
}
